
/***************************************************************************
 *   fecab 
 *                                                                         *
 *   Copyright (C) 2010 by kuerant                                         *
 *                         <kuerant@gmail.com>                             *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This library is distributed in the hope that it will be usefull, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
 *   Library General Public License for more details.                      *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston,                *
 *   MA 02111-1307, USA                                                    *
 ***************************************************************************/

#include <cstring>
#include <iostream>

#include <openssl/md5.h>

#include <boost/date_time/posix_time/posix_time.hpp>

#include "bocat/common/logging.hpp"

#include "fecab/store.hpp"


using namespace boost::posix_time;


#define	CONFIG_FILE	"config.ini"


bocat::common::Logger*	logger_p = NULL;


static  u_int32_t   
getMD5( const char* symbol, u_int32_t symbol_size, char* pszBufOut, u_int32_t sizeBufOut )
{
    unsigned char   md5[16];

    memset( md5, 0, sizeof(md5) );

    MD5_CTX ctx;

    MD5_Init( &ctx );
    MD5_Update( &ctx, symbol, symbol_size );
    MD5_Final( md5, &ctx );

    memcpy( pszBufOut, md5, 16 );

    return 16;
}

static	u_int32_t
getPIN( const char* symbol, u_int32_t symbol_size )
{
	char	md5[32];
	
	memset(md5, 0, sizeof(md5));
	u_int32_t	md5_size = getMD5( symbol, symbol_size, md5, sizeof(md5) );

	u_int32_t	pin;

	memcpy( (char *)&pin, &md5[2], sizeof(u_int32_t) );

	return pin;
}

void	test_put( fecab::Store* store_p, u_int32_t key_begin, u_int32_t key_end );
void	test_get( fecab::Store* store_p, u_int32_t key_begin, u_int32_t key_end );
void	test_del( fecab::Store* store_p, u_int32_t key_begin, u_int32_t key_end );

void	basic_test( fecab::Store* store_p );

int	main(void)
{
	logger_p = bocat::common::getLogger();

	try {
		fecab::StoreManager	manager( CONFIG_FILE );

		fecab::Store*	store_p = manager.getStore();

		//basic_test( store_p );

		u_int32_t	period_size = 1024 * 1024 * 8;
		for (u_int32_t k=0; k<100000000; k += period_size) {
			test_put( store_p, k, k + period_size );

			test_get( store_p, 0, k + period_size );
		}

		for (u_int32_t k=0; k<100000000; k += period_size) {
			test_del( store_p, k, k + period_size );
		}
	}
	catch(std::exception& e) {
		std::cerr << "ERROR : " << e.what() << std::endl;
		return 1;
	}

	std::cerr << "done." << std::endl;
	return	0;
}


float
getTimeDuration( ptime time_begin, ptime time_end )
{
	time_period	tp( time_begin, time_end );
	time_duration	td = tp.length();
	long	time_last_ms = td.total_milliseconds();
	float	time_last_s = time_last_ms / 1000.0f;

	return time_last_s;
}


void	test_put( fecab::Store* store_p, u_int32_t key_begin, u_int32_t key_end )
{
	ptime	time_start = microsec_clock::local_time();

	int		round_records = 100000;
	int		count_down = round_records;
	ptime	time_start_R = microsec_clock::local_time();

	for ( u_int32_t k=key_begin; k<key_end; k++ ) {
			char	key[32];
			u_int32_t	key_size = snprintf(key, sizeof(key) - 1, "key-%09u", k);
			char	data[32];
			u_int32_t	data_size = snprintf(data, sizeof(data) - 1, "data-%09u", k);

			u_int32_t	pin = getPIN( key, key_size );
			store_p->put( pin, key, key_size, data, data_size );

			--count_down;
			if (count_down < 1) {
				ptime	time_stop_R = microsec_clock::local_time();
				float	time_last_s = getTimeDuration( time_start_R, time_stop_R );
				float	tps = round_records / time_last_s;
				logger_p->info( __LINE__, __FILE__, " k=%u, insert %d records last %.2f seconds, R-tps=%.2f", k, round_records, time_last_s, tps );

				count_down = round_records;
				time_start_R = microsec_clock::local_time();
			}
	}

	ptime	time_stop = microsec_clock::local_time();
	float	time_last_s = getTimeDuration( time_start, time_stop );
	float	tps = (key_end - key_begin) / time_last_s;
	logger_p->info( __LINE__, __FILE__, "insert [%.2e .. %.2e) last %.2f seconds, tps=%.2f", (double)key_begin, (double)key_end, time_last_s, tps );
}

void	test_get( fecab::Store* store_p, u_int32_t key_begin, u_int32_t key_end )
{
	ptime	time_start = microsec_clock::local_time();

	int		round_records = 100000;
	int		count_down = round_records;
	ptime	time_start_R = microsec_clock::local_time();

	for ( u_int32_t k=key_begin; k<key_end; k++ ) {
			char	key[32];
			u_int32_t	key_size = snprintf(key, sizeof(key) - 1, "key-%09u", k);
			char	data[32];
			u_int32_t	data_size = snprintf(data, sizeof(data) - 1, "data-%09u", k);

			char	buf[32];
			memset(buf, 0, sizeof(buf));

			u_int32_t	pin = getPIN( key, key_size );
			store_p->get( pin, key, key_size, buf, sizeof(buf) );
			if (0 != strcmp(buf, data)) {
				std::cerr << "bad data for key '" << key << "' got '" << buf << "', but want '" << data << "'\n";
				break;
			}

			--count_down;
			if (count_down < 1) {
				ptime	time_stop_R = microsec_clock::local_time();
				float	time_last_s = getTimeDuration( time_start_R, time_stop_R );
				float	tps = round_records / time_last_s;
				logger_p->info( __LINE__, __FILE__, " k=%u, get %d records last %.2f seconds, R-tps=%.2f", k, round_records, time_last_s, tps );

				count_down = round_records;
				time_start_R = microsec_clock::local_time();
			}
	}

	ptime	time_stop = microsec_clock::local_time();
	float	time_last_s = getTimeDuration( time_start, time_stop );
	float	tps = (key_end - key_begin) / time_last_s;
	logger_p->info( __LINE__, __FILE__, "get [%.2e .. %.2e) last %.2f seconds, tps=%.2f", (double)key_begin, (double)key_end, time_last_s, tps );
}



void	test_del( fecab::Store* store_p, u_int32_t key_begin, u_int32_t key_end )
{
	ptime	time_start = microsec_clock::local_time();

	int		round_records = 100000;
	int		count_down = round_records;
	ptime	time_start_R = microsec_clock::local_time();

	for ( u_int32_t k=key_begin; k<key_end; k++ ) {
			char	key[32];
			u_int32_t	key_size = snprintf(key, sizeof(key) - 1, "key-%09u", k);

			u_int32_t	pin = getPIN( key, key_size );
			store_p->erase( pin, key, key_size );

			--count_down;
			if (count_down < 1) {
				ptime	time_stop_R = microsec_clock::local_time();
				float	time_last_s = getTimeDuration( time_start_R, time_stop_R );
				float	tps = round_records / time_last_s;
				logger_p->info( __LINE__, __FILE__, " k=%u, del %d records last %.2f seconds, R-tps=%.2f", k, round_records, time_last_s, tps );

				count_down = round_records;
				time_start_R = microsec_clock::local_time();
			}
	}

	ptime	time_stop = microsec_clock::local_time();
	float	time_last_s = getTimeDuration( time_start, time_stop );
	float	tps = (key_end - key_begin) / time_last_s;
	logger_p->info( __LINE__, __FILE__, "del [%.2e .. %.2e) last %.2f seconds, tps=%.2f", (double)key_begin, (double)key_end, time_last_s, tps );
}



void	basic_test( fecab::Store* store_p )
{
		const char*	TABLE = "table_test";
		u_int32_t	records = 10;

		std::cerr << "put records into table ..." << std::endl;
		for( u_int32_t i=0; i<records; i++) {
			char	key[32];
			u_int32_t	key_size = snprintf(key, sizeof(key) - 1, "key-%09u", i);
			char	data[32];
			u_int32_t	data_size = snprintf(data, sizeof(data) - 1, "data-%09u", i);

			store_p->put( TABLE, key, key_size, data, data_size );
		}
		std::cerr << "put records into table done." << std::endl;

		std::cerr << "get records from table ..." << std::endl;
		for( u_int32_t i=0; i<records; i++) {
			char	key[32];
			u_int32_t	key_size = snprintf(key, sizeof(key) - 1, "key-%09u", i);
			char	data[32];
			u_int32_t	data_size = snprintf(data, sizeof(data) - 1, "data-%09u", i);

			char	buf[32];
			memset(buf, 0, sizeof(buf));

			store_p->get( TABLE, key, key_size, buf, sizeof(buf) );
			if (0 != strcmp(buf, data)) {
				std::cerr << "bad data for key '" << key << "' got '" << buf << "', but want '" << data << "'\n";
			}
		}
		std::cerr << "get records from table done." << std::endl;


		std::cerr << "erase records from table ..." << std::endl;
		for( u_int32_t i=0; i<records; i++) {
			char	key[32];
			u_int32_t	key_size = snprintf(key, sizeof(key) - 1, "key-%09u", i);

			store_p->erase( TABLE, key, key_size );
		}
		std::cerr << "erase records from table done." << std::endl;

		std::cerr << "get records from table after erasing..." << std::endl;
		for( u_int32_t i=0; i<records; i++) {
			char	key[32];
			u_int32_t	key_size = snprintf(key, sizeof(key) - 1, "key-%09u", i);

			char	buf[32];
			memset(buf, 0, sizeof(buf));

			u_int32_t size = store_p->get( TABLE, key, key_size, buf, sizeof(buf) );
			if (size > 0) {
				std::cerr << "bad data for key '" << key << "' got '" << buf << "', but want NULL\n";
			}
		}
		std::cerr << "get records from table after erasing done." << std::endl;

		std::cerr << "erase records from table after ereasing ..." << std::endl;
		for( u_int32_t i=0; i<records; i++) {
			char	key[32];
			u_int32_t	key_size = snprintf(key, sizeof(key) - 1, "key-%09u", i);

			store_p->erase( TABLE, key, key_size );
		}
		std::cerr << "erase records from table after ereasing done." << std::endl;
}


